import socket
import threading
import os
import sys
from collections import defaultdict
import platform
import mimetypes
import time
from pathlib import Path
import hashlib  # 导入hashlib模块

class MyException(Exception):
    pass

class Server(object):
    # 对象实例化时进行的初始化
    def __init__(self, HOST='', PORT=7777, Version='1.10'):
        self.Host = HOST
        self.Port = PORT
        self.Version = Version
        self.FileList = []  # 文件列表，包括
        self.lock = threading.Lock()

    # start listenning
    def start(self):
        try:
            self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            self.s.bind((self.Host, self.Port))
            self.s.listen(5)
            print('Server %s %s is listening on port %s' % (
                socket.gethostbyname(socket.gethostname()), self.Version, self.Port))

            while True:
                soc, addr = self.s.accept()
                print('%s:%s connected' % (addr[0], addr[1]))
                thread = threading.Thread(
                    target=self.handler, args=(soc, addr))
                thread.start()
        except KeyboardInterrupt:
            print('\nShutting down the server..\nGood Bye!')
            try:
                sys.exit(0)
            except SystemExit:
                os._exit(0)

    # connect with a client
    # 处理客户端发过来的请求
    def handler(self, soc, addr):
        # keep Server received request from client
        host = None
        port = None
        while True:
            try:
                # sleep(1)
                exceptionMethod  = ''
                req = soc.recv(1024).decode()
                print('\nServer received request:\n%s' % req)
                lines = req.splitlines()
                version = lines[0].split()[-1]
                if version != self.Version:
                    soc.sendall(str.encode(self.Version +
                                           ' 505 P2P-CI Version Not Supported\n'))
                else:
                    method = lines[0].split()[0]
                    if method == 'ADD':
                        exceptionMethod = 'ADD'
                        ipStr = lines[1].split(None, 1)[1]
                        portStr = int(lines[2].split(None, 1)[1])
                        # dictFileA = Code20_P2P_Comm.dictFileType.copy()
                        fileName = lines[0].split()[2]
                        fileHash = lines[0].split()[3]
                        self.addToBlockList(
                            soc, (ipStr, portStr), fileName, fileHash)
                    elif method == 'LIST':
                        exceptionMethod = 'LIST'
                        self.getAllFromFileList(soc)
                    elif method == 'EMPTY':
                        exceptionMethod = 'EMPTY'
                        self.emptyFileList(soc)    
                    else:
                        raise AttributeError('Method Not Match')
            except ConnectionError:
                print('%s:%s left' % (addr[0], addr[1]))
                # Clean data if necessary
                if host and port:
                    self.removeFailedConn(host, port)
                soc.close()
                break
            except BaseException:
                try:
                    soc.sendall(str.encode(
                        self.Version + '  400 Bad Request at %s.(%s)\n' % (exceptionMethod,time.time())))
                except ConnectionError:
                    print('%s:%s left.(%s)' % (addr[0], addr[1],time.time()))
                    # Clean data if necessary
                    if host and port:
                        self.removeFailedConn(host, port)
                    soc.close()
                    break

    def removeFailedConn(self, host, port):
        self.lock.acquire()
        for x in self.FileList:
            if (x[1], x[2]) == (host, port):
                self.FileList.remove(x)
        self.lock.release()

    def addToBlockList(self, soc, peer, fileName, fileHash):
        # 将clientA拥有的文件加入到server的文件列表中
        # 其他clientB可以请求服务器，获得文件列表。clientB可以通过P2P连接到clientA，下载clientA拥有的文件
        self.lock.acquire()
        try:
            x = [fileName, fileHash, peer[0], peer[1]]
            if x not in self.FileList:
                self.FileList.append([fileName, fileHash, peer[0], peer[1]])
        finally:
            self.lock.release()
        header = self.Version + ' 200 OK\n'
        header += 'add block %s %s %s %s\n' % (fileName,
                                            fileHash, peer[0], peer[1])
        soc.sendall(str.encode(header))
        print(header)

    def emptyFileList(self,soc ):
        # 将clientA拥有的文件加入到server的文件列表中
        # 其他clientB可以请求服务器，获得文件列表。clientB可以通过P2P连接到clientA，下载clientA拥有的文件
        self.lock.acquire()
        try:
            self.FileList.clear()
        finally:
            self.lock.release()
        header = self.Version + ' 200 OK\n'
        header += 'Empty\n' 
        soc.sendall(str.encode(header))
        print(header)

    def getAllFromFileList(self, soc):
        self.lock.acquire()
        try:
            if len(self.FileList) == 0:
                header = self.Version + ' 404 Not Found\n'
            else:
                header = self.Version + ' 200 OK\n'
                for x in self.FileList:
                    # fileName,fileHash,hostname,port
                    header += 'block %s %s %s %s\n' % (x[0], x[1], x[2], x[3])
        finally:
            self.lock.release()
        soc.sendall(str.encode(header))
        print(header)


s = Server()
s.start()
